﻿using NCS.IConnect.Core.IoC;
using NCS.IConnect.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http.Dependencies;

namespace NCS.IConnect.Core.WebApi
{
    /// <summary>
    /// A custom DependencyResolver which uses ServiceLocator to acticvate service.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
    public class ServiceLocatableDependencyResolver : IDependencyResolver
    {
        #region Fileds
        private List<IDisposable> disposableServices = new List<IDisposable>();
        #endregion

        #region Properties
        /// <summary>
        /// Gets the service locator.
        /// </summary>
        /// <value>
        /// The service locator.
        /// </value>
        public IServiceLocator ServiceLocator { get; private set; }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceLocatableDependencyResolver"/> class.
        /// </summary>
        /// <param name="serviceLocatorName">Name of the service locator.</param>
        public ServiceLocatableDependencyResolver(string serviceLocatorName = null)
        {
            this.ServiceLocator = ServiceLocatorFactory.GetServiceLocator(serviceLocatorName);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceLocatableDependencyResolver"/> class.
        /// </summary>
        /// <param name="serviceLocator">The service locator.</param>
        public ServiceLocatableDependencyResolver(IServiceLocator serviceLocator)
        {
            Guard.ArgumentNotNull(serviceLocator, "serviceLocator");
            this.ServiceLocator = serviceLocator;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Begins the scope.
        /// </summary>
        /// <returns>The created DependencyScope.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public IDependencyScope BeginScope()
        {
            return new ServiceLocatableDependencyResolver(this.ServiceLocator);
        }

        /// <summary>
        /// Gets the service.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns>The activated service instance.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public object GetService(Type serviceType)
        {
            Guard.ArgumentNotNull(serviceType, "serviceType");
            try
            {
                object service = this.ServiceLocator.GetService(serviceType);
                this.AddDisposableService(service);
                return service;
            }
            catch (ResolutionException)
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the services.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns>The activated service instances.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public IEnumerable<object> GetServices(Type serviceType)
        {
            Guard.ArgumentNotNull(serviceType, "serviceType");
            try
            {
                List<object> list = new List<object>();
                foreach (var service in this.ServiceLocator.GetAllServices(serviceType))
                {
                    this.AddDisposableService(service);
                    list.Add(service);
                }
                return list;
            }
            catch (ResolutionException)
            {
                return Enumerable.Empty<object>();
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
        public void Dispose()
        {
            foreach (IDisposable disposable in disposableServices)
            {
                disposable.Dispose();
            }
        }
        #endregion

        #region Private Methods
        private void AddDisposableService(object servie)
        {
            IDisposable disposable = servie as IDisposable;
            if (null != disposable && !disposableServices.Contains(disposable))
            {
                disposableServices.Add(disposable);
            }
        }
        #endregion
    }
}
